package com.xgzit.xgzmall.search.thread;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * 多线程测试
 *
 * @Author xgz
 * @Date 2021-05-10 14:08
 */

@SuppressWarnings("AlibabaRemoveCommentedCode")
@Slf4j
public class ThreadTest {
    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("main... start...");
        /*CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
        }, executor);*/

       /* CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 0;
            log.info("运行结果：" + i);
            return i;
        }, executor)
                .whenComplete((res,exception)->{
                    // 虽然能得到异常信息，但是不能修改返回数据
                   log.info("异步任务成功完成了！结果是："+res+" 异常是："+exception);
                })
                .exceptionally(throwable -> {
                    // 感知异常，同时返回默认值
                    return 10;
                });*/
        /** 方法执行完成后的处理，可以在异常发生后返回默认值 **/
        /*CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
            return i;
        }, executor)
                .handle((res, exception)->{
                    if (res!=null) {
                        return res;
                    }
                    if (exception != null) {
                        return 0;
                    }
                    return -1;
                });*/
        /**
         * 带了 async 表示异步，不带表示 用同一个线程
         * thenRun 不能感知上一步执行结果，无返回值
         * thenAccept 能接收上一步结果，但是没有返回值
         * thenApplyAsync 能接收上一步结果，有返回值
         * */
        /*CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
            return i;
        }, executor)
                .thenApplyAsync((res) -> {
                    log.info("任务2启动了，上一次结果是：" + res);
                    return -1;
                }, executor);*/
        /**
         * 任务都要完成
         * runAfterBoth 不接收组合的任务的结果，也没有返回值；只干活
         * thenAcceptBothAsync  接收组合的2个任务的结果，没有返回值
         * thenCombineAsync 接收组合的2个任务的结果，有返回值
         **/
        /*CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
            return i;
        }, executor);
        CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            return "hello";
        }, executor);*/

        /*future1.runAfterBoth(future2,()->{
            log.info("runAfterBoth");
        });*/
        /*future1.thenAcceptBothAsync(future2,(res1,res2)->{
            log.info("结果1："+res1+"结果2："+res2+"等这2货都完成了，我异步干活！");
        });*/
       /* CompletableFuture<Integer> future3 = future1.thenCombineAsync(future2, (res1, res2) -> {
            log.info("结果1：" + res1 + "结果2：" + res2 + "  等这2货都完成了，我异步干活,还有返回值");
            return -1;
        });*/
        /**
         * 2个任务有一个完成就执行任务3
         * runAfterEitherAsync 不接收组合的任务的结果，也没有返回值；只干活
         * acceptEitherAsync  接收组合的2个任务中先完成的结果，没有返回值
         * applyToEitherAsync   接收组合的2个任务中先完成的结果，有返回值
         **/
        /*future1.runAfterEitherAsync(future2,()->{
            log.info("runAfterEitherAsync");
        },executor);*/
        /*future1.acceptEitherAsync(future2,(res)->{
            log.info("结果："+res+"  等这2货有1个完成了，我异步干活！");
        },executor);*/
        /*CompletableFuture<Integer> future3 = future1.applyToEitherAsync(future2, (res) -> {
            log.info("结果：" + res + "  等这2货有1个完成了，我异步干活, 还有返回值");
            return -1;
        }, executor);*/
        /**
         * 多任务组合
         * allof    等待所有任务全部完成
         * anyof    有一个任务完成
         **/
        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
            log.info("future01查询商品图片信息");
            return "hello.jpg";
        }, executor);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            log.info("future02查询商品属性信息");
            return "黑色+256G";
        }, executor);
        CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
            log.info("future03查询商品介绍");
            return "华为";
        }, executor);

//        CompletableFuture<Void> allOf = CompletableFuture.allOf(future01, future02, future03);
//        allOf.get(); // 等待全部完成
        // 获取任务自己的结果, 调用自己的get方法。
//        future01.get();

        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future01, future02, future03);
        anyOf.get(); // 获取成功的那个任务的结果
        log.info("main... end...");
    }

    public void threadTest(String[] args) throws ExecutionException, InterruptedException {
        log.info("main... start...");
        /**
         * Thread 开启线程
         * 1、继承Thread
         *      Thread01 thread01 = new Thread01();
         *      thread01.start();
         * 2、实现Runnable接口
         *      Runable01 runable01 = new Runable01();
         *      new Thread(runable01).start();
         * 3、实现Callable 接口 + FutureTase 可以拿到返回结果，可以处理异常
         *      FutureTask<Integer> task = new FutureTask<>(new Callable01());
         *      new Thread(task).start();
         *      阻塞等待 等待整个线程执行完成，获取返回结果
         *      Integer integer = task.get();
         * 4、线程池【ExecutorService】   给线程池提交任务
         *      1、创建：
         *          1）、Excutors
         *          2)、
         * 区别： 1、2 不能得到返回值  第三种 可以获取返回值 但是 只有线程池的方式才可以控制资源
         *      Future 可以获取异步执行结果
         */

        /**
         * 线程池 7大参数：
         * corePoolSize： 核心线程数； 线程池创建好之后就准备就绪的线程数量，等待接收任务执行。一直存在，除非设置了允许核心线程超时参数
         * maximumPoolSize：池中允许的最大线程数；控制资源的
         * keepAliveTime：存活时间；当前线程数量大于核心数量，当线程空闲时，等待超过这个时间就会释放线程【核心线程不释放】。
         * TimeUnit unit：时间单位
         * BlockingQueue<Runnable> workQueue：阻塞的任务队列；任务很多时，多的任务放在队列里面排队。等待空闲线程来取任务执行。
         * ThreadFactory threadFactory：线程工厂; 创建线程的工厂
         * RejectedExecutionHandler handler：处理队列满了情况；按照指定的拒绝策略拒绝执行任务
         *
         * 工作流程：
         *  1、线程池创建好，准备好核心数量corePoolSize的线程，等待执行任务。
         *      1.1、当核心线程都在执行任务，就将新进来的任务放入阻塞队列workQueue，当有空闲的核心线程时，自己去队列取任务执行
         *      1.2、核心线程无空闲，阻塞队列也满了，就直接开启新线程，新线程最大数量 maximumPoolSize；
         *      1.3、核心线程无空闲，阻塞队列也满了，maximumPoolSize 也满了，就会使用 handler 拒绝策略
         *      1.4、核心线程无空闲，maximumPoolSize有很多空闲，就会 在 keepAliveTime 时间单位unit后进行线程释放。如 10，TimeUnit.SECONDS  表示10秒
         *      new LinkedBlockingDeque<>() 默认是 Integer 的最大值，容易导致内存不足，一定要传入 业务定制数量，比如通过压力测试得出
         *
         * 例子： 核心7个，最大20，队列50，有100任务，怎么执行的？
         *  核心7个会立即执行，队列放50，再开启 13个 线程达到最大，取 13个任务，队列减少13，再加上13， 还剩 30 使用拒绝策略
         *  如果不想抛弃，可以使用CallerRunsPolicy 策略
         **/
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

//        Executors.newCachedThreadPool();  // 没有核心线程保留，可以创建很多线程，线程空闲就会被清空
//        Executors.newFixedThreadPool(10); // 固定池大小的线程池，核心线程与最大线程一致，就是这么多。也不会被回收。
//        Executors.newScheduledThreadPool(); // 定时任务的线程池
//        Executors.newSingleThreadExecutor();    // 单线程的线程池，只有一个线程从队列里面一个个执行任务
        log.info("main... end...");
        // 线程池 只有1,2个，每一个异步任务提交给线程池
        executor.execute(new Runable01());

    }

    /**
     * 方式1：实现Runnable接口
     **/
    public static class Thread01 extends Thread {

        @Override
        public void run() {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
        }
    }

    /**
     * 方式2：继承Thread 重新run方法
     **/
    public static class Runable01 implements Runnable {

        @Override
        public void run() {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
        }
    }

    /**
     * 方式3：实现Callable 接口 + FutureTase
     **/
    public static class Callable01 implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            log.info("当前线程Id:" + Thread.currentThread().getId());
            int i = 10 / 2;
            log.info("运行结果：" + i);
            return i;
        }
    }

}
